﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

namespace QuestFramework
{
    public class Camera
    {
        private static Camera instance = null;
        private static Object classLock = typeof(Camera);
        private float viewportAspectRatio;
        private Matrix world;
        private Matrix view;
        private Matrix projection;
        private Vector3 position;
        private Vector3 target;
        private Vector3 rotation;
        private Vector3 upAxis;
        private Vector3 lookAxis;
        private Vector3 rightAxis;
        private float nearClipping;
        private float farClipping;
        private float moveSpeed;

        private Camera()
        {
            position = new Vector3(0, 0, 0);
            target = new Vector3(0, 0, 0);
            upAxis = new Vector3(0, 1, 0);
            lookAxis = new Vector3(0, 0, -1);
            rightAxis = new Vector3(1, 0, 0);
            nearClipping = 0.5f;
            farClipping = 200000.0f;
            moveSpeed = 20.0f;
        }

        public static Camera Instance
        {
            get
            {
                lock (classLock)
                {
                    if (instance == null)
                    {
                        instance = new Camera();
                    }

                    return instance;
                }
            }
        }

        public Matrix World
        {
            get { return world; }
        }

        public Matrix View
        {
            get { return view; }
        }

        public Matrix Projection
        {
            get { return projection; }
        }

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        public Vector3 Target
        {
            get { return target; }
        }

        public Vector3 Rotation
        {
            get { return rotation; }
            set { rotation = value; }
        }

        public float RotationX
        {
            get { return rotation.X; }
            set { rotation.X = value; }
        }

        public float RotationY
        {
            get { return rotation.Y; }
            set { rotation.Y = value; }
        }

        public float RotationZ
        {
            get { return rotation.Z; }
            set { rotation.Z = value; }
        }

        public Vector3 UpAxis
        {
            get { return upAxis; }
        }

        public Vector3 LookAxis
        {
            get { return lookAxis; }
        }

        public Vector3 RightAxis
        {
            get { return rightAxis; }
        }

        public float MoveSpeed
        {
            get { return moveSpeed; }
            set { moveSpeed = value; }
        }

        public void Initialize(float aspectRatio)
        {
            viewportAspectRatio = aspectRatio;
        }

        public void Initialize(float aspectRatio, Vector3 cameraPosition)
        {
            Initialize(aspectRatio);
            position = cameraPosition;
        }

        public void Initialize(float aspectRatio, Vector3 cameraPosition, Vector3 cameraTarget)
        {
            Initialize(aspectRatio, cameraPosition);
            target = cameraTarget;
        }
        
        public void Update()
        {
            Matrix rollMatrix = Matrix.CreateFromAxisAngle(lookAxis, rotation.Z);
            upAxis = Vector3.Transform(upAxis, rollMatrix);
            rightAxis = Vector3.Transform(rightAxis, rollMatrix);

            Matrix yawMatrix = Matrix.CreateFromAxisAngle(upAxis, rotation.Y);
            lookAxis = Vector3.Transform(lookAxis, yawMatrix);
            rightAxis = Vector3.Transform(rightAxis, yawMatrix);

            Matrix pitchMatrix = Matrix.CreateFromAxisAngle(rightAxis, rotation.X);
            lookAxis = Vector3.Transform(lookAxis, pitchMatrix);
            upAxis = Vector3.Transform(upAxis, pitchMatrix);

            target = position + lookAxis;

            world = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);
            view = Matrix.CreateLookAt(position, target, Vector3.Up);
            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4
                , viewportAspectRatio, nearClipping, farClipping);
        }
    }
}
